package org.eclipse.swt.widgets;

import com.mephi.oshs.model.entry.IEntry;
import com.mephi.oshs.model.tree.ITree;
import com.mephi.oshs.model.tree.OshsTree;
import com.mephi.oshs.view.elements.IOshsTreeWidget;
import org.eclipse.swt.SWT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class EntryTree<T extends IEntry> extends Tree implements IOshsTreeWidget<T> {
    private static final Logger logger = LoggerFactory.getLogger(EntryTree.class);

    private final Map<T, java.util.List<EntryTreeItem<T>>> searchMap = new HashMap<T, java.util.List<EntryTreeItem<T>>>();

    public EntryTree(Composite parent, int style) {
        super(parent, style);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void update(final T updatedEntry) {
        final java.util.List<EntryTreeItem<T>> entryTreeItemList = this.findEntries(updatedEntry);
        for (EntryTreeItem<T> entryTreeItem : entryTreeItemList) {
            entryTreeItem.update();
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public java.util.List<EntryTreeItem<T>> findEntries(final T searchEntry) {
        return this.searchMap.get(searchEntry);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean selectEntry(final T entry, final int index) {
        final java.util.List<EntryTreeItem<T>> entryTreeItemList = this.findEntries(entry);
        if (index < entryTreeItemList.size()) {
            logger.debug("selectEntry(): Found entries for select in entry tree view: " + entryTreeItemList.size());

            final EntryTreeItem<T> item = entryTreeItemList.get(index);
            this.setSelection(item);

            final Event selectEvent = new Event();
            selectEvent.item = item;
            this.notifyListeners(SWT.Selection, selectEvent);
            return true;
        }
        return false;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void addSelectListener(Listener listener) {
        this.addListener(SWT.Selection, listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setTree(final OshsTree<T> oshsTree) {

        // Отключаем перерисовку, пока будем добавлять новые элементы.
        this.setRedraw(false);

        this.removeAll();
        final T entry = oshsTree.getHead();
        final TreeItem topTreeItem = new EntryTreeItem<T>(this, entry);

        for (final ITree<T> entryTree : oshsTree.getSubTrees()) {
            this.updateOshsTree(entryTree, topTreeItem);
        }

        // Включаем перерисовку после добавления новых элементов.
        this.setRedraw(true);
    }

    private void updateOshsTree(final ITree<T> oshsTree, final TreeItem topTreeItem) {
        final T entry = oshsTree.getHead();
        final EntryTreeItem<T> treeItem = new EntryTreeItem<T>(topTreeItem, entry);

        if (!this.searchMap.containsKey(entry)) {
            this.searchMap.put(entry, new ArrayList<EntryTreeItem<T>>());
        }
        this.searchMap.get(entry).add(treeItem);

        for (final ITree<T> entryTree : oshsTree.getSubTrees()) {
            this.updateOshsTree(entryTree, treeItem);
        }
    }

    private void findEntriesRecursive(final TreeItem[] treeItems, final T searchEntry,
                                      final java.util.List<EntryTreeItem<T>> entryTreeItemList) {
        for (final TreeItem treeItem : treeItems) {
            if (treeItem instanceof EntryTreeItem<?>) {
                final T entry = ((EntryTreeItem<T>) treeItem).getEntry();
                if (entry.equals(searchEntry)) {
                    entryTreeItemList.add((EntryTreeItem<T>) treeItem);
                    return;
                }
            }

            // Recursion.
            this.findEntriesRecursive(treeItem.getItems(), searchEntry, entryTreeItemList);
        }
    }
}
